package app

import (
	"fmt"
	"gin-demo/pkg/logging"
	protoutils "gin-demo/pkg/proto"
	"github.com/astaxie/beego/validation"
	"github.com/gin-gonic/gin"
	"github.com/golang/protobuf/jsonpb"
	"github.com/golang/protobuf/proto"
	"github.com/ikaiguang/go-pagination"
	"net/http"
	"reflect"
	"strconv"

	"gin-demo/pkg/e"
)

// BindPbReq pb req
// MethodGet get 方法比较特殊，请按照实际方法来获取参数
func BindPbReq(ctx *gin.Context, req proto.Message) (err error) {
	switch ctx.Request.Method {
	case http.MethodGet: // 未实现
		//return BindGetPb(ctx, req)
		return err
	default: // post
		return BindPostPb(ctx, req)
	}
}

// BindPostPb http method post
func BindPostPb(ctx *gin.Context, req proto.Message) (err error) {
	// request body
	//buf, err := ioutil.ReadAll(ctx.Request.Body)
	//if err != nil {
	//	logging.Error(err)
	//	err = NewResponseErr(http.StatusOK, e.ERROR, nil)
	//	return
	//}

	// req
	if err = jsonpb.Unmarshal(ctx.Request.Body, req); err != nil {
		logging.Error(err)
		err = NewResponseErr(http.StatusOK, e.InvalidParams, nil)
		return
	}
	return
}

// BindPagingOption 获取分页参数
func BindPagingOption(ctx *gin.Context) (opt *pagination.PagingOption) {
	opt = pagination.DefaultPagingOption()

	// page size
	opt.PageSize, _ = strconv.ParseInt(ctx.Query("limit"), 10, 64)
	if opt.PageSize <= 0 {
		opt.PageSize = 20
	}
	if opt.PageSize > 500 {
		opt.PageSize = 20
	}

	// page number
	opt.GotoPageNumber, _ = strconv.ParseInt(ctx.Query("page"), 10, 64)
	if opt.GotoPageNumber <= 0 {
		opt.GotoPageNumber = 1
	}

	// order
	//orderStr, _ := ctx.GetQuery("key")

	return opt
}

// BindGetPb http method get
// 未实现完毕
func BindGetPb(ctx *gin.Context, req proto.Message) (err error) {
	// reflect.Value
	sValue := reflect.ValueOf(req)
	sType := reflect.TypeOf(req)

	// is pointer
	if sValue.Kind() == reflect.Ptr {
		sValue = sValue.Elem()
		sType = sType.Elem()
	}

	// bad request parameters : s isnot struct
	if sValue.Kind() != reflect.Struct {
		err = NewResponseErr(http.StatusOK, e.InvalidParams, nil)
		return
	}

	// query values
	values := ctx.Request.URL.Query()

	// find field
	for i := 0; i < sValue.NumField(); i++ {
		field := sValue.Field(i)
		// 不能设置
		if !field.CanSet() {
			continue
		}

		// field name
		//fmt.Println(sType.Field(i).Name)
		fieldName := protoutils.GetProtoKey(sType.Field(i).Tag.Get(protoutils.ProtoTagName))
		if _, ok := values[fieldName]; !ok {
			continue
		}
		//field.SetInt()
		//field.SetUint()
		//field.SetBool()
		//field.SetFloat()
		//field.SetString()
		fmt.Println(fieldName)
		fmt.Println(field.Kind())
		//ctx.ShouldBind()
	}

	fmt.Println(req)

	return
}

// BindAndValid binds and validates data
func BindAndValid(c *gin.Context, form interface{}) (int, int) {
	err := c.Bind(form)
	if err != nil {
		return http.StatusBadRequest, e.InvalidParams
	}

	valid := validation.Validation{}
	check, err := valid.Valid(form)
	if err != nil {
		return http.StatusInternalServerError, e.ERROR
	}
	if !check {
		MarkErrors(valid.Errors)
		return http.StatusBadRequest, e.InvalidParams
	}

	return http.StatusOK, e.SUCCESS
}
